/*
 * Copyright (c) 2009, Hamish Morgan. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the University of Sussex nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package locusts.common;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;

/**
 * Instances of the Player class hold information about a single user of the
 * game and allow changes to be made to that user. For example allow the name
 * to be changed, or the score to be increased.
 * 
 * @author Hamish Morgan
 */
public class Player extends AbstractUniqueObject implements Serializable,
        ChangeListenable {

    /**
     * Hold all listeners to this class
     */
    private transient EventListenerList listenerList = new EventListenerList();
    /**
     * The change event is always the same, so create it lazily and cache it
     * for later use.
     */
    private ChangeEvent changeEvent = null;
//    private static final long serialVersionUID = 1L;
    //
    private transient static final String DEFAULT_NAME =
            "Unnamed Player";
    private transient static final int DEFAULT_SCORE = 0;
    private transient static final double DEFAULT_CROPS = 100D;
    //
    private String name;
    private int kills;
    private double damage;
    private double time;
//    private double score;
//    private double crops;
    private int farmId;
    private transient boolean modified;
    //

    public Player(int id, String name) {
        super(id);
        if (name == null)
            throw new NullPointerException();
        this.name = name;
        kills = 0;
        damage = 0;
        time = 0;
        farmId = -1;
        modified = false;
    }

    public Player(String name) {
        super();
        if (name == null)
            throw new NullPointerException();
        this.name = name;
        kills = 0;
        damage = 0;
        time = 0;
        farmId = -1;
        modified = false;
    }

    public Player() {
        this(DEFAULT_NAME);
    }

    protected Player(Player other) {
        super(other);
        this.name = other.name;
        this.time = other.time;
        this.kills = other.kills;
        this.damage = other.damage;
        this.farmId = other.farmId;
        this.modified = other.modified;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeObject(name);
        out.writeInt(kills);
        out.writeDouble(damage);
        out.writeDouble(time);
        out.writeInt(farmId);
    }

    private void readObject(ObjectInputStream in) throws IOException,
            ClassNotFoundException {
        this.name = (String) in.readObject();
        this.kills = in.readInt();
        this.damage = in.readDouble();
        this.time = in.readDouble();
        this.farmId = in.readInt();
        listenerList = new EventListenerList();
        modified = false;
    }
//
    public void setModified(boolean modified) {
        this.modified = modified;
    }
//
    public boolean isModified() {
        return modified;
    }

    public int getFarmId() {
        return farmId;
    }

    public boolean isCropPileSet() {
        return farmId != -1;
    }

    public void setFarmId(int farmId) {
        if (this.farmId != farmId) {
            this.farmId = farmId;
            fireStateChangedEvent();
        }
    }

    public int getKills() {
        return kills;
    }

    public double getDamage() {
        return damage;
    }

    public double getTime() {
        return time;
    }

    public void setKills(int kills) {
        if (this.kills != kills) {
            this.kills = kills;
            fireStateChangedEvent();
//            setModified(modified);
        }
    }

    public void setDamage(double damage) {
        if (this.damage != damage) {
            this.damage = damage;
            fireStateChangedEvent();
//            setModified(modified);
        }
    }

    public void setTime(double time) {
        if (this.time != time) {
            this.time = time;
            fireStateChangedEvent();
//            setModified(modified);
        }
    }

    public void addKills(int amount) {
        setKills(getKills() + amount);
    }

    public void addDamage(double amount) {
        setDamage(getDamage() + amount);
    }

    public void addTime(double amount) {
        setTime(getTime() + amount);
    }
//
//    public double getCrops() {
//        return crops;
//    }
//
//    public void setCrops(double crops) {
//        if (this.crops != crops) {
//            this.crops = crops;
//            fireStateChangedEvent();
//        }
//    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (!this.name.equals(name)) {
            this.name = name;
            fireStateChangedEvent();
        }
    }

    public double getScore() {
        return getKills() * 100
                + getDamage()
                + getTime();
    }

//    public void setScore(double score) {
//        if (score != this.score) {
//            this.score = score;
//            fireStateChangedEvent();
//        }
//    }
//
//    public void addScore(double amount) {
//        setScore(getScore() + amount);
//    }
    /**
     * Player #id (Name:, Crops:, Score: )
     * @return
     */
    @Override
    public String toString() {
        final StringBuffer buf = new StringBuffer();
        buf.append("Player #");
        buf.append(getId());
        buf.append(" (Name: ");
        buf.append(name);
//        buf.append(", Crops: ");
//        buf.append(crops);
        buf.append(", Score: ");
        buf.append(getScore());
        buf.append(")");
        return buf.toString();
    }

    @Override
    public Object clone() {
        return new Player(this);
    }

    public void addChangeListener(ChangeListener listener) {
        listenerList.add(ChangeListener.class, listener);
    }

    public void removeChangeListener(ChangeListener listener) {
        listenerList.remove(ChangeListener.class, listener);
    }

    public ChangeListener[] getChangeListeners() {
        return listenerList.getListeners(ChangeListener.class);
    }

    public int getChangeListenerCount() {
        return listenerList.getListenerCount(ChangeListener.class);
    }

    protected void fireStateChangedEvent() {
        setModified(modified);
        final Object[] listeners = listenerList.getListenerList();
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ChangeListener.class) {
                if (changeEvent == null)
                    changeEvent = new ChangeEvent(this);
                ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
            }
        }
    }
}
